package com.qianyu.versioncontrolcore.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qianyu.versioncontrolcore.entity.base.BaseIdDTO;
import com.qianyu.versioncontrolcore.entity.bean.VersionAreaConfig;
import com.qianyu.versioncontrolcore.entity.bean.VersionConf;
import com.qianyu.versioncontrolcore.entity.dto.RegisterNumDto;
import com.qianyu.versioncontrolcore.entity.dto.VersionConfDto;
import com.qianyu.versioncontrolcore.entity.vo.AreaVo;
import com.qianyu.versioncontrolcore.entity.vo.VersionAreaVo;
import com.qianyu.versioncontrolcore.entity.vo.VersionConfVo;
import com.qianyu.versioncontrolcore.exception.CustomerException;
import com.qianyu.versioncontrolcore.mapper.VersionConfMapper;
import com.qianyu.versioncontrolcore.service.AreaService;
import com.qianyu.versioncontrolcore.service.VersionAreaConfigService;
import com.qianyu.versioncontrolcore.service.VersionConfService;
import com.qianyu.versioncontrolcore.util.CacheKeyUtil;
import com.qianyu.versioncontrolcore.util.HttpKit;
import com.qianyu.versioncontrolcore.util.Object2MapUtil;
import com.qianyu.versioncontrolcore.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.qianyu.versioncontrolcore.enumerate.ResultEnum.VERSION_IS_EXIST;

@Service
@RequiredArgsConstructor
public class VersionConfServiceImpl extends ServiceImpl<VersionConfMapper, VersionConf> implements VersionConfService {


    private final RedisUtil redisUtil;
    private final AreaService areaService;
    private final VersionAreaConfigService versionAreaConfigService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveVersion(VersionConfDto.VersionConfSaveDto dto) {
        validateVersion(null, dto.getVersion());
        VersionConf conf = new VersionConf();
        BeanUtils.copyProperties(dto, conf);
        saveOrUpdate(conf);
        if (redisUtil.hasKey(CacheKeyUtil.versionConfSortedSet())) {
            redisUtil.del(CacheKeyUtil.versionConfSortedSet());
        }
        updateCache();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVersion(VersionConfDto.VersionConfUpdateDto dto) {
        validateVersion(dto.getId(), dto.getVersion());
        VersionConf conf = new VersionConf();
        BeanUtils.copyProperties(dto, conf);
        saveOrUpdate(conf);
        if (redisUtil.hasKey(CacheKeyUtil.versionConfSortedSet())) {
            redisUtil.del(CacheKeyUtil.versionConfSortedSet());
        }
        updateCache();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteVersion(BaseIdDTO dto) {
        update(Wrappers.<VersionConf>lambdaUpdate().set(VersionConf::getStatus, 0).eq(VersionConf::getId, dto.getId()));
        if (redisUtil.hasKey(CacheKeyUtil.versionConfSortedSet())) {
            redisUtil.del(CacheKeyUtil.versionConfSortedSet());
        }
        updateCache();
    }

    @Override
    public VersionConfVo.VersionConfigInfoVo getOne(BaseIdDTO dto) {
        VersionConf versionConf = getById(dto.getId());
        VersionConfVo.VersionConfigInfoVo infoVo = BeanUtil.copyProperties(versionConf, VersionConfVo.VersionConfigInfoVo.class);

        List<VersionAreaConfig> list = versionAreaConfigService.getList(infoVo.getVersion());
        List<VersionConfVo.VersionArea> area = BeanUtil.copyToList(list, VersionConfVo.VersionArea.class);
        infoVo.setAreas(area);
        return infoVo;
    }

    @Override
    public List<VersionConfVo.VersionListVo> getVersionList() {
        LambdaQueryWrapper<VersionConf> wrapper = Wrappers.lambdaQuery();
        wrapper.select(VersionConf::getVersion).eq(VersionConf::getStatus, 1).orderByDesc(VersionConf::getCreateTime);
        List<VersionConf> versionConfs = baseMapper.selectList(wrapper);
        return BeanUtil.copyToList(versionConfs, VersionConfVo.VersionListVo.class);
    }


    @Override
    public Page<VersionConf> pageList(VersionConfDto.VersionConfPageDto dto) {
        LambdaQueryWrapper<VersionConf> wrapper = Wrappers.lambdaQuery();
        if (!ObjectUtils.isEmpty(dto.getVersion())) {
            wrapper.like(VersionConf::getVersion, dto.getVersion());
        }
        wrapper.eq(VersionConf::getStatus, 1);
        wrapper.orderByDesc(VersionConf::getCreateTime);
        IPage<VersionConf> page = page(new Page<>(dto.getPageNum(), dto.getPageSize()), wrapper);
        page.getRecords().forEach(v -> {
            v.setNum(notifyRegister(v.getVersion(), null, 0));
        });

        return (Page<VersionConf>) page;
    }

    @Override
    public VersionAreaVo getVersion(String code) {

        List<AreaVo.AreaListVo> listVos = areaService.listAreaByProvince(code);

        //获取所有城市配置
        List<VersionAreaConfig> list = versionAreaConfigService.getAllList();


        //分组
        Map<String, List<VersionAreaConfig>> listMap = list.stream().collect(Collectors.groupingBy(VersionAreaConfig::getVersion));

        //获取该省级下的所有地级市
        List<String> codes = listVos.stream().map(AreaVo.AreaListVo::getAdcode).collect(Collectors.toList());
        Collections.shuffle(codes);
        VersionAreaVo vo = new VersionAreaVo();

        if (redisUtil.hasKey(CacheKeyUtil.versionConfSortedSet())) {
            Object o = redisUtil.get(CacheKeyUtil.versionConfSortedSet());
            List<VersionConf> versionConfVos = (List<VersionConf>) o;
            //根据优先级分组
            Map<Integer, List<VersionConf>> map = versionConfVos.stream().collect(Collectors.groupingBy(VersionConf::getPriority));
            ArrayList<Integer> integers = new ArrayList<>(map.keySet());
            //优先级降序
            Collections.reverse(integers);
            for (Integer i : integers) {
                //获取优先级高的并打乱顺序
                List<VersionConf> voList = map.get(i);
                Collections.shuffle(voList);
                for (VersionConf conf : voList) {
                    //验证是否在有效期
                    if (redisUtil.hasKey(conf.getVersion())) {
                        //判断是否在间隔秒内
                        if (!redisUtil.hasKey(CacheKeyUtil.versionConfSpace(conf.getVersion()))) {
                            if (listMap.containsKey(conf.getVersion())) {
                                return getResult(listMap, conf, i, codes);
                            }

                        }
                    }

                }

            }

        } else {
            LambdaQueryWrapper<VersionConf> wrapper = Wrappers.lambdaQuery();
            wrapper.select(VersionConf::getVersion, VersionConf::getTime, VersionConf::getBurst, VersionConf::getPriority, VersionConf::getSpace)
                    .eq(VersionConf::getStatus, 1).orderByDesc(VersionConf::getPriority);
            List<VersionConf> configs = baseMapper.selectList(wrapper);
            Iterator<VersionConf> iterator = configs.iterator();
            while (iterator.hasNext()) {
                VersionConf conf = iterator.next();
                Integer count = getRegisterNum(new RegisterNumDto(conf.getVersion(), conf.getTime()));
                if (count > conf.getBurst()) {
                    iterator.remove();
                }
                redisUtil.set(conf.getVersion(), count, conf.getTime());
            }
            Collections.shuffle(configs);
            for (VersionConf conf : configs) {
                redisUtil.set(CacheKeyUtil.versionConfSortedSet(), configs, 600);
                if(!redisUtil.hasKey(CacheKeyUtil.versionConfSpace(conf.getVersion()))) {
                    redisUtil.set(CacheKeyUtil.versionConfSpace(conf.getVersion()), conf.getPriority(), conf.getSpace());
                }
                return getResult(listMap, conf, null, codes);
            }
        }
        return vo;
    }


    public VersionAreaVo getResult(Map<String, List<VersionAreaConfig>> listMap, VersionConf conf, Integer i, List<String> codes) {
        VersionAreaVo vo = new VersionAreaVo();
        List<VersionAreaConfig> configs = listMap.get(conf.getVersion());
        //筛选
        Map<String, VersionAreaConfig> configMap = configs.stream().collect(Collectors.toMap(VersionAreaConfig::getAdcode, config -> config));
        for (String adcode : codes) {
            VersionAreaConfig config = configMap.get(adcode);
            if (!redisUtil.hasKey(CacheKeyUtil.versionAreaConfigSpace(conf.getVersion(), adcode))) {
                if (config != null && config.getVersion().equals(conf.getVersion())) {
                    //添加该城市该版本获取间隔时间
                    redisUtil.set(CacheKeyUtil.versionAreaConfigSpace(conf.getVersion(), adcode), config.getNumber(), config.getSpace());
                    vo.setVersion(conf.getVersion());
                    vo.setAdcode(adcode);
                    //添加版本获取间隔时间
                    redisUtil.set(CacheKeyUtil.versionConfSpace(conf.getVersion()), i, conf.getSpace());
                    return vo;


                }
            } else {

                Object num = redisUtil.get(CacheKeyUtil.versionAreaConfigSpace(conf.getVersion(), adcode));
                Integer number = (Integer) num;
                if (number > 0) {
                    //设置该城市该版本可注册数减一
                    redisUtil.set(CacheKeyUtil.versionAreaConfigSpace(conf.getVersion(), adcode), number - 1, config.getSpace());
                    vo.setVersion(conf.getVersion());
                    vo.setAdcode(adcode);
                    //更新版本获取间隔时间
                    redisUtil.set(CacheKeyUtil.versionConfSpace(conf.getVersion()), i, conf.getSpace());
                    return vo;
                }
            }

        }
        return vo;

    }

    @Override
    public Integer getRegisterNum(RegisterNumDto dto) {
        try {
            Map<String, String> map = Object2MapUtil.bean2Map(dto);
            String resp = HttpKit.get("http://118.178.125.179:7203/v1/account/query_reg_num", map);
            JSONObject jsonObject = JSONObject.parseObject(resp);
            return jsonObject.getInteger("data");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateRedisCache(String version) {

        if (redisUtil.setNxEx(version, 10)) {
            //todo
        } else {
            Object o = redisUtil.get(version);
            Integer count = (Integer) o;
            redisUtil.set(version, count + 1);
        }
    }

    @Override
    public Integer notifyRegister(String version, String adcode, Integer type) {
        VersionConf versionConf = baseMapper.selectOne(Wrappers.<VersionConf>lambdaQuery().select(VersionConf::getTime).eq(VersionConf::getVersion, version));
        if (type == 0) {
            if (!redisUtil.hasKey(version)) {
                Integer i = getRegisterNum(new RegisterNumDto(version, versionConf.getTime()));
                redisUtil.set(version, versionConf.getTime());
                return i;
            } else {
                Object o = redisUtil.get(version);
                return (Integer) o;
            }
        } else {
            if (!redisUtil.hasKey(version)) {
                Integer i = getRegisterNum(new RegisterNumDto(version, versionConf.getTime()));
                redisUtil.set(version, i, versionConf.getTime());
            } else {
                Object o = redisUtil.get(version);
                Integer count = (Integer) o;
                redisUtil.set(version, count + 1);
            }

            if (adcode != null) {
                if (redisUtil.hasKey(CacheKeyUtil.versionAreaConfigSpace(version, adcode))) {
                    Object o = redisUtil.get(CacheKeyUtil.versionAreaConfigSpace(version, adcode));
                    Integer count = (Integer) o;
                    redisUtil.set(CacheKeyUtil.versionAreaConfigSpace(version, adcode), count - 1);
                }
            }
        }
        return 0;
    }

    private void updateCache() {
        LambdaQueryWrapper<VersionConf> wrapper = Wrappers.lambdaQuery();
        wrapper.select(VersionConf::getVersion, VersionConf::getTime, VersionConf::getBurst, VersionConf::getPriority, VersionConf::getSpace).eq(VersionConf::getStatus, 1).orderByDesc(VersionConf::getPriority);
        List<VersionConf> configs = baseMapper.selectList(wrapper);
        for (VersionConf vc : configs) {
            Integer count = getRegisterNum(new RegisterNumDto(vc.getVersion(), vc.getTime()));
            if (count > vc.getBurst()) {
                configs.remove(vc);
            }
            if (!redisUtil.hasKey(vc.getVersion())) {
                redisUtil.set(vc.getVersion(), count, vc.getTime());
            }
        }
        redisUtil.set(CacheKeyUtil.versionConfSortedSet(), configs, 600);
    }


    private void validateVersion(Long id, String version) {
        VersionConf one = baseMapper.selectOne(Wrappers.<VersionConf>lambdaQuery().eq(VersionConf::getVersion, version).ne(!ObjectUtils.isEmpty(id), VersionConf::getId, id));
        if (!ObjectUtils.isEmpty(one)) {
            throw new CustomerException(VERSION_IS_EXIST);
        }
    }
}
